<!-- FaceRegister.vue -->
<template>
  <!-- 页面主容器 -->
  <div style="max-width:700px;margin:40px auto;padding:20px;">
    <h1>人脸识别</h1>

    <!-- 摄像头操作区域 -->
    <div>
      <!-- 摄像头开关按钮 -->
      <div style="margin-bottom:10px;">
        <button @click="startCamera" :disabled="isCameraOn">开启摄像头</button>
        <button @click="stopCamera" :disabled="!isCameraOn">关闭摄像头</button>
      </div>

      <!-- 视频预览窗口 -->
      <video
          ref="videoRef"
          autoplay
          playsinline
          muted
          style="width:100%;max-width:640px;border:1px solid #ddd"
      />

      <!-- 实时检测控制区域 -->
      <div style="margin-top:20px;">
        <label>
          实时检测帧率：
          <input v-model.number="fpsInput" type="number" min="1" max="15" style="width:60px">
          帧/秒
        </label><br>
        <button @click="startSending" :disabled="!isCameraOn||isDetecting">开始实时检测</button>
        <button @click="stopSending" :disabled="!isDetecting">停止检测</button>
        <p v-if="isDetecting" style="color:green;margin-top:5px">正在实时检测...</p>
      </div>

      <!-- 拍照按钮 -->
      <div style="margin-top:20px">
        <button @click="takePhoto" :disabled="!isCameraOn">拍照</button>
      </div>

      <!-- 错误提示信息 -->
      <p v-if="error" style="color:red;margin-top:10px">{{ error }}</p>
    </div>

    <!-- 拍照预览区域 -->
    <div v-if="capturedImage" style="margin-top:20px;text-align:center">
      <h3>拍摄预览</h3>
      <img :src="capturedImage" alt="拍摄的人脸" style="max-width:300px;border:1px solid #ccc">
      <div style="margin-top:10px">
        <button @click="recognizeFromPhoto" :disabled="isSubmitting">
          {{ isSubmitting ? '识别中...' : '开始识别' }}
        </button>
        <button @click="retake" style="margin-left:10px">重新拍照</button>
      </div>
    </div>

    <!-- 识别结果展示区域 -->
    <div
        style="margin-top:20px;padding:15px;background:#f9f9f9;border:1px solid #eee;border-radius:6px;text-align:center;font-size:16px">
      <strong>返回的 ID：</strong> {{ recognizedId }}
    </div>

    <!-- 隐藏画布，用于图像处理 -->
    <canvas ref="canvasRef" style="display:none"/>
  </div>
</template>

<script setup>

// 导入 Vue 的响应式 API 和生命周期钩子
import {ref, onUnmounted} from 'vue'
// 导入人脸识别 API
import {userPostFaceInfoPicB} from '@/api/user.js'


// 视频元素引用
const videoRef = ref(null)
// 画布元素引用
const canvasRef = ref(null)
// 摄像头状态标识
const isCameraOn = ref(false)
// 错误信息
const error = ref('')
// 拍照预览图像 URL
const capturedImage = ref('')
// 存储拍照生成的 Blob 数据
let photoBlob = null
// 存储媒体流对象
let stream = null

// WebSocket 连接对象
let ws = null
// 定时器 ID
let intervalId = null
// 实时检测状态标识
const isDetecting = ref(false)
// 实时检测帧率控制
const fpsInput = ref(2)

// 提交状态标识
const isSubmitting = ref(false)
// 识别到的用户 ID
const recognizedId = ref('未识别到用户')


/**
 * 启动摄像头功能
 * 请求用户摄像头权限并显示视频流
 */
const startCamera = async () => {
  error.value = ''
  capturedImage.value = ''
  photoBlob = null
  try {
    // 请求获取用户媒体设备（摄像头）
    stream = await navigator.mediaDevices.getUserMedia({
      video: {width: 640, height: 480, facingMode: 'user'},
      audio: false
    })
    // 将视频流绑定到 video 元素
    videoRef.value.srcObject = stream
    isCameraOn.value = true
  } catch (err) {
    // 处理不同类型的错误
    error.value =
        err.name === 'NotAllowedError'
            ? '用户拒绝了摄像头权限，请在浏览器设置中开启'
            : err.name === 'NotFoundError'
                ? '未检测到摄像头设备'
                : `无法访问摄像头：${err.message}`
    console.error('摄像头错误:', err)
  }
}

/**
 * 关闭摄像头功能
 * 停止所有媒体轨道并清理资源
 */
const stopCamera = () => {
  stopSending()
  if (stream) {
    // 停止所有媒体轨道
    stream.getTracks().forEach(t => t.stop())
    stream = null
  }
  // 更新摄像头状态
  isCameraOn.value = false
}


/**
 * 拍照功能
 * 从视频流中截取当前帧并转换为图像
 */
const takePhoto = () => {
  if (!isCameraOn.value) return
  const video = videoRef.value
  const canvas = canvasRef.value
  const ctx = canvas.getContext('2d')
  // 设置画布尺寸与视频一致
  canvas.width = video.videoWidth
  canvas.height = video.videoHeight
  // 将视频当前帧绘制到画布上
  ctx.drawImage(video, 0, 0)

  // 释放上一次生成的 blob URL
  if (capturedImage.value) URL.revokeObjectURL(capturedImage.value)

  // 将画布内容转换为 JPEG 格式的 Blob
  canvas.toBlob(blob => {
    photoBlob = blob
    capturedImage.value = URL.createObjectURL(blob)
  }, 'image/jpeg', 0.8)
}

/**
 * 重新拍照功能
 * 清除当前拍摄的图像并重置相关状态
 */
const retake = () => {
  if (capturedImage.value) URL.revokeObjectURL(capturedImage.value)
  capturedImage.value = ''
  photoBlob = null
}


/**
 * 建立 WebSocket 连接
 * 连接到人脸识别服务进行实时检测
 */
const connectWebSocket = () => {
  // 如果已经连接则直接返回
  if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return
  // 创建 WebSocket 连接
  ws = new WebSocket('ws://zyqc-server.bzyd.dpdns.org/ws/detect')
  ws.onopen = () => (isDetecting.value = true)
  ws.onmessage = e => { /* 结果不渲染 */
  }
  ws.onclose = () => (isDetecting.value = false)
  ws.onerror = err => console.error('WS 错误:', err)
}

/**
 * 捕获当前帧并发送至 WebSocket 服务
 * 用于实时人脸识别检测
 */
const captureAndSend = () => {
  const video = videoRef.value
  const canvas = canvasRef.value
  const ctx = canvas.getContext('2d')
  // 确保视频元素有有效尺寸
  if (!video.videoWidth || !video.videoHeight) return
  // 设置画布尺寸与视频一致
  canvas.width = video.videoWidth
  canvas.height = video.videoHeight
  // 将视频当前帧绘制到画布上
  ctx.drawImage(video, 0, 0)
  // 将画布内容转换为 Blob 并发送
  canvas.toBlob(async blob => {
    if (ws?.readyState === WebSocket.OPEN) ws.send(await blob.arrayBuffer())
  }, 'image/jpeg', 0.7)
}

/**
 * 开始实时检测
 * 建立连接并按设定帧率发送视频帧
 */
const startSending = () => {
  if (!isCameraOn.value) return alert('请先开启摄像头')
  connectWebSocket()
  // 计算发送间隔（毫秒）
  const interval = 1000 / fpsInput.value
  intervalId && clearInterval(intervalId)
  // 定时发送视频帧
  intervalId = setInterval(captureAndSend, interval)
}

/**
 * 停止实时检测
 * 清理定时器并关闭 WebSocket 连接
 */
const stopSending = () => {
  intervalId && clearInterval(intervalId)
  intervalId = null
  ws && ws.close()
  ws = null
  isDetecting.value = false
}


/**
 * 从照片进行人脸识别
 * 将拍摄的照片发送至后端进行识别并获取用户 ID
 */
const recognizeFromPhoto = async () => {
  if (!photoBlob) return alert('请先拍照')
  isSubmitting.value = true
  recognizedId.value = '识别中...'
  try {
    // 调用后端 API 进行人脸识别
    const data = await userPostFaceInfoPicB(photoBlob)
    console.log('原始返回:', JSON.stringify(data, null, 2))

    // 解析返回数据获取用户 ID
    let userId = null
    if (data?.identification_results?.length) {
      const first = data.identification_results[0]
      userId = first.user_id || first.id || first.userId
    }
    if (!userId && data?.user_id) userId = data.user_id
    if (!userId && data?.results?.length) {
      userId = data.results[0].user_id || data.results[0].id
    }
    recognizedId.value = userId || '未识别到用户'
  } catch (err) {
    console.error('识别失败:', err)
    recognizedId.value = '识别失败'
  } finally {
    isSubmitting.value = false
  }
}

/**
 * 组件卸载时的清理工作
 * 释放资源并关闭连接
 */
onUnmounted(() => {
  if (capturedImage.value) URL.revokeObjectURL(capturedImage.value)
  stopCamera()
  stopSending()
})
</script>

<style scoped lang="scss">
// 页面标题样式
h1 {
  text-align: center;
  font-size: 50px;
  margin-bottom: 20px;
  color: #333;
}

// 按钮样式
button {
  padding: 8px 16px;
  margin-right: 10px;
  background: #409eff;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;

  // 禁用状态样式
  &:disabled {
    background: #ccc;
    cursor: not-allowed;
  }

  // 悬停状态样式（非禁用时）
  &:hover:not(:disabled) {
    background: #3080dd;
  }
}

// 数字输入框样式
input[type="number"] {
  padding: 5px;
  width: 60px;
  text-align: center;
}
</style>